﻿using DotConsulting.VisualStudio.BuildOutputCopy.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;

namespace DotConsulting.VisualStudio.BuildOutputCopy.ViewModel
{


    public class BuildOutputCopyViewModel : INotifyPropertyChanged
    {
        public SolutionBuildInfo BuildInfo { get; private set; }

        public string ActiveConfiguration
        {
            get
            {
                return string.Format("{0} - {1}", BuildInfo.ActiveConfiguration, BuildInfo.ActivePlatform);
            }
        }

        private string _msgInfo;
        public string MessageInfo
        {
            get
            {
                return _msgInfo;
            }
            set
            {
                _msgInfo = value;
                OnPropertyChanged("MessageInfo");
            }
        }

        private MessageState _msgInfoState;
        public MessageState MessageInfoState
        {
            get
            {
                return _msgInfoState;
            }
            set
            {
                _msgInfoState = value;
                OnPropertyChanged("MessageInfoState");
            }
        }

        private void SetMessageInfo(string text, bool isError)
        {
            MessageInfo = text;
            if (isError)
            {
                MessageInfoState = MessageState.Error;
            }
            else
            {
                MessageInfoState = MessageState.Ok;
            }

        }

        private string _destinationFolder;
        public string DestinationFolder
        {
            get
            {
                return _destinationFolder;
            }
            set
            {
                _destinationFolder = value;
                OnPropertyChanged("DestinationFolder");
            }
        }

        private bool _busy;
        public bool Busy
        {
            get
            {
                return _busy;
            }
            set
            {
                _busy = value;
                OnPropertyChanged("Busy");
            }
        }

        int _currentProgress;
        public int CurrentProgress
        {
            get
            {
                return _currentProgress;
            }
            set
            {
                _currentProgress = value;
                OnPropertyChanged("CurrentProgress");

            }
        }

        int _maxProgress;
        public int MaxProgress
        {
            get
            {
                return _maxProgress;
            }
            set
            {
                _maxProgress = value;
                OnPropertyChanged("MaxProgress");

            }
        }

        int _totalSelected;
        public int TotalSelected
        {
            get
            {
                return _totalSelected;
            }
            set
            {
                _totalSelected = value;
                OnPropertyChanged("TotalSelected");
            }
        }

        public int TotalFiles { get; private set; }


        public BuildOutputCopyViewModel(EnvDTE.Solution dteSolution)
        {

            try
            {

                Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;

                BuildInfo = new SolutionBuildInfo(dteSolution);

                AutoSelectFiles();

                CopyFilesCommand = new DelegateCommand((param) => DoFilesCopy());

                BrowseDirectoryCommand = new DelegateCommand((param) => DoBrowseDirectory());

                CheckChangedCommand = new DelegateCommand((param) => DoCheckChanged(param));

                RefreshCommand = new DelegateCommand((param) => DoRefresh());

                NextSearchResultCommand = new DelegateCommand((param) => GoToNextSearchResult(), (param) => IsGoToNextSearchResultAllowed());

                PreviousSearchResultCommand = new DelegateCommand((param) => GoToPreviousSearchResult(), (param) => IsGoToPreviousSearchResultAllowed());

                CheckFoundCommand = new DelegateCommand((param) => CheckFound(), (param) => SearchResultCount > 0);

                UncheckFoundCommand = new DelegateCommand((param) => UncheckFound(), (param) => SearchResultCount > 0);
            }
            finally
            {
                Mouse.OverrideCursor = null;
            }
        }



        private void AutoSelectFiles()
        {

            // Pre-select all dlls and exes
            _totalSelected = 0;
            TotalFiles = 0;

            foreach (var item in AllOutputFiles)
            {
                if (item != null)
                {
                    TotalFiles++;
                    if (item.FileInfo.Extension.ToLowerInvariant() == ".exe"
                        || item.FileInfo.Extension.ToLowerInvariant() == ".dll")
                    {
                        item.Selected = true;
                        _totalSelected++;
                    }
                }
            }

            OnPropertyChanged("TotalFiles");
            OnPropertyChanged("TotalSelected");
        }

        private void DoRefresh()
        {

            Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;

            BuildInfo.Refresh();

            AutoSelectFiles();

            MessageInfo = string.Empty;

            //CommandManager.InvalidateRequerySuggested();

            OnPropertyChanged("ActiveConfiguration");
            OnPropertyChanged("AllOutputFiles");

            DoSearch();

            Mouse.OverrideCursor = null;
        }

        private void DoCheckChanged(Object p)
        {
            var box = p as System.Windows.Controls.CheckBox;
            if (box.IsChecked.HasValue && box.IsChecked.Value)
            {
                TotalSelected++;
            }
            else
            {
                TotalSelected--;
            }
        }

        public ICommand RefreshCommand
        {
            get;
            private set;
        }

        public ICommand CopyFilesCommand
        {
            get;
            private set;
        }

        public ICommand BrowseDirectoryCommand
        {
            get;
            private set;
        }

        public ICommand CheckChangedCommand
        {
            get;
            private set;
        }


        public ICommand PreviousSearchResultCommand
        {
            get;
            private set;
        }

        public ICommand NextSearchResultCommand
        {
            get;
            private set;
        }


        public ICommand CheckFoundCommand
        {
            get;
            private set;
        }

        public ICommand UncheckFoundCommand
        {
            get;
            private set;
        }

        private void DoBrowseDirectory()
        {
            using (var browser = new FolderBrowserDialog())
            {
                var result = browser.ShowDialog();
                if (result == DialogResult.OK)
                {
                    DestinationFolder = browser.SelectedPath;
                }
            }
        }

        private void DoFilesCopy()
        {

            var filesToCopy = AllOutputFiles.Where(f => f.Selected == true).ToList();

            if (filesToCopy.Count == 0)
            {
                System.Windows.MessageBox.Show("No files selected!", Resources.DIALOG_TITLE, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                return;
            }

            MessageInfo = string.Empty;

            if (string.IsNullOrWhiteSpace(DestinationFolder))
            {
                System.Windows.MessageBox.Show("Destination folder not selected!", Resources.DIALOG_TITLE, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                return;
            }

            var di = new DirectoryInfo(DestinationFolder);

            if (di.Exists == false)
            {
                var answer = System.Windows.MessageBox.Show("Destination folder doesn't exist.\nDo you want me to create it ?", Resources.DIALOG_TITLE, MessageBoxButton.YesNo, MessageBoxImage.Warning);
                if (answer == MessageBoxResult.No)
                {
                    return;
                }

                di.Create();
            }
            else
            {
                var answer = System.Windows.MessageBox.Show("Clean destination folder?\n(Anyway copy will override any existing files)", Resources.DIALOG_TITLE, MessageBoxButton.YesNo, MessageBoxImage.Warning);
                if (answer == MessageBoxResult.Yes)
                {
                    try
                    {
                        foreach (var f in di.EnumerateFiles("*.*", SearchOption.AllDirectories))
                        {
                            f.Delete();
                        }

                        foreach (var d in di.EnumerateDirectories())
                        {
                            d.Delete();
                        }
                    }
                    catch (Exception ex)
                    {
                        string msg = string.Format("Failed to empty the folder!\n(Error: {0})\n\nDo you want to continue?", ex.Message);
                        var continueAnswer = System.Windows.MessageBox.Show(msg, Resources.DIALOG_TITLE, MessageBoxButton.YesNo, MessageBoxImage.Warning);
                        if (continueAnswer == MessageBoxResult.No)
                        {
                            return;
                        }
                    }
                }
            }


            MaxProgress = filesToCopy.Count;
            CurrentProgress = 0;


            Task.Factory.StartNew(() =>
            {

                try
                {
                    Busy = true;

                    var startTime = DateTime.Now;

                    foreach (var f in filesToCopy)
                    {

                        f.FileInfo.CopyTo(Path.Combine(DestinationFolder, f.FileInfo.Name), true);
                        CurrentProgress += 1;
                    }

                    var elapseInSeconds = Math.Ceiling(DateTime.Now.Subtract(startTime).TotalSeconds);

                    var msg = string.Format("Done. {0} file(s) copied. (Started at {1:HH:mm:ss} - Took {2} seconds)", CurrentProgress, startTime, elapseInSeconds);
                    SetMessageInfo(msg, false);
                }
                catch (Exception ex)
                {
                    var msg = string.Format("Error! ({0})", ex.Message);
                    SetMessageInfo(msg, true);
                }
                finally
                {
                    Busy = false;
                }
            });

        }

        public IEnumerable<OutputFileInfo> AllOutputFiles
        {
            get
            {
                if (BuildInfo.ProjectsBuildInfo != null)
                {
                    foreach (var p in BuildInfo.ProjectsBuildInfo)
                    {
                        foreach (var f in p.OutputFiles)
                        {
                            yield return f;
                        }
                    }
                }
                else
                {
                    yield break;
                }
            }
        }


        #region Search/Filter

        private OutputFileInfo _selectedItem;
        public OutputFileInfo SelectedItem
        {
            get
            {
                return _selectedItem;
            }
            set
            {
                _selectedItem = value;
                OnPropertyChanged("SelectedItem");
            }
        }

        private string _searchText;
        public string SearchText
        {
            get
            {
                return _searchText;
            }
            set
            {
                _searchText = value;
                DoSearch();
            }
        }

        private SearchScope _searchScope = SearchScope.All;
        public SearchScope SearchScope
        {
            get
            {
                return _searchScope;
            }
            set
            {
                _searchScope = value;
                DoSearch();
            }
        }

        private void DoSearch()
        {
            ResetSearch();

            if (string.IsNullOrWhiteSpace(SearchText))
            {
                return;
            }

            foreach (OutputFileInfo ofi in AllOutputFiles)
            {

                if (SearchScope == SearchScope.All ||
                    (SearchScope == SearchScope.Selected && ofi.Selected) ||
                    (SearchScope == SearchScope.Unselected && !ofi.Selected))
                {

                    if (ofi.Filename.IndexOf(SearchText, 0, StringComparison.OrdinalIgnoreCase) != -1)
                    {
                        _searchResult.Add(ofi);
                    }
                }
            }

            OnPropertyChanged("SearchResultCount");

            if (_searchResult.Count != 0)
            {
                SelectedItem = _searchResult[0];
                _searchResultIndex = 0;
            }
        }


        private List<OutputFileInfo> _searchResult = new List<OutputFileInfo>();

        public int SearchResultCount
        {
            get
            {
                return _searchResult.Count;
            }
        }

        private int _searchResultIndex;

        private void ResetSearch()
        {
            _searchResult.Clear();
            SelectedItem = null;
            OnPropertyChanged("SearchResultCount");
        }


        private bool IsGoToPreviousSearchResultAllowed()
        {
            return (SearchResultCount > 0 && _searchResultIndex > 0);
        }

        private void GoToPreviousSearchResult()
        {
            if (_searchResultIndex > 0)
            {
                _searchResultIndex--;
                SelectedItem = _searchResult[_searchResultIndex];
            }
        }

        private bool IsGoToNextSearchResultAllowed()
        {
            return (SearchResultCount > 0 && _searchResultIndex < SearchResultCount - 1);
        }

        private void GoToNextSearchResult()
        {
            if (_searchResultIndex < SearchResultCount - 1)
            {
                _searchResultIndex++;
                SelectedItem = _searchResult[_searchResultIndex];
            }

        }

        private void CheckFound()
        {
            if (SearchResultCount > 0)
            {
                try
                {

                    Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
                    foreach (var item in _searchResult)
                    {
                        if (item.Selected == false)
                        {
                            item.Selected = true;
                            TotalSelected++;
                        }
                    }
                    DoSearch();
                }
                finally
                {
                    Mouse.OverrideCursor = null;
                }
            }
        }

        private void UncheckFound()
        {
            if (SearchResultCount > 0)
            {
                try
                {

                    Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;

                    foreach (var item in _searchResult)
                    {
                        if (item.Selected == true)
                        {
                            item.Selected = false;
                            TotalSelected--;
                        }
                    }
                    DoSearch();
                }
                finally
                {
                    Mouse.OverrideCursor = null;
                }

            }
        }

        #endregion


        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
            //CommandManager.InvalidateRequerySuggested();
        }
    }
}
